/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.csp.sentinel.dashboard.controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.csp.sentinel.util.StringUtil;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;
import com.alibaba.csp.sentinel.dashboard.domain.vo.MetricVo;

/**
 * 实时监控
 * @author leyou
 */
@Controller
@RequestMapping(value = "/metric", produces = MediaType.APPLICATION_JSON_VALUE)
public class MetricController {

    private static Logger logger = LoggerFactory.getLogger(MetricController.class);

    private static final long maxQueryIntervalMs = 1000 * 60 * 60;

    @Autowired
    private MetricsRepository<MetricEntity> metricStore;

    @ResponseBody
    @RequestMapping("/queryTopResourceMetric.json")
    public Result<?> queryTopResourceMetric(final String app,
                                            Integer pageIndex,
                                            Integer pageSize,
                                            Boolean desc,
                                            Long startTime, Long endTime, String searchKey) {
        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (pageIndex == null || pageIndex <= 0) {
            pageIndex = 1;
        }
        if (pageSize == null) {
            pageSize = 6;
        }
        if (pageSize >= 20) {
            pageSize = 20;
        }
        if (desc == null) {
            desc = true;
        }
        if (endTime == null) {
            endTime = System.currentTimeMillis();
        }
        if (startTime == null) {
            startTime = endTime - 1000 * 60 * 5;
        }
        if (endTime - startTime > maxQueryIntervalMs) {
            return Result.ofFail(-1, "time intervalMs is too big, must <= 1h");
        }
        List<String> resources = metricStore.listResourcesOfApp(app);
        logger.debug("queryTopResourceMetric(), resources.size()={}", resources.size());

        if (resources == null || resources.isEmpty()) {
            return Result.ofSuccess(null);
        }
        if (!desc) {
            Collections.reverse(resources);
        }
        if (StringUtil.isNotEmpty(searchKey)) {
            List<String> searched = new ArrayList<>();
            for (String resource : resources) {
                if (resource.contains(searchKey)) {
                    searched.add(resource);
                }
            }
            resources = searched;
        }
        int totalPage = (resources.size() + pageSize - 1) / pageSize;
        List<String> topResource = new ArrayList<>();
        if (pageIndex <= totalPage) {
            topResource = resources.subList((pageIndex - 1) * pageSize,
                Math.min(pageIndex * pageSize, resources.size()));
        }
        final Map<String, Iterable<MetricVo>> map = new ConcurrentHashMap<>();
        logger.debug("topResource={}", topResource);
        long time = System.currentTimeMillis();
        for (final String resource : topResource) {
            List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(
                app, resource, startTime, endTime);
            logger.debug("resource={}, entities.size()={}", resource, entities == null ? "null" : entities.size());
            List<MetricVo> vos = MetricVo.fromMetricEntities(entities, resource);
            Iterable<MetricVo> vosSorted = sortMetricVoAndDistinct(vos);
            map.put(resource, vosSorted);
        }
        logger.debug("queryTopResourceMetric() total query time={} ms", System.currentTimeMillis() - time);
        Map<String, Object> resultMap = new HashMap<>(16);
        resultMap.put("totalCount", resources.size());
        resultMap.put("totalPage", totalPage);
        resultMap.put("pageIndex", pageIndex);
        resultMap.put("pageSize", pageSize);

        Map<String, Iterable<MetricVo>> map2 = new LinkedHashMap<>();
        // order matters.
        for (String identity : topResource) {
            map2.put(identity, map.get(identity));
        }
        resultMap.put("metric", map2);
        return Result.ofSuccess(resultMap);
    }



    @ResponseBody
    @RequestMapping("/queryTopResourceMetricList.json")
    public Result<?> queryTopResourceMetricList(final String app,
                                            Integer pageIndex,
                                            Integer pageSize,
                                            Boolean desc,
                                            Long startTime, Long endTime, String searchKey) {
        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (pageIndex == null || pageIndex <= 0) {
            pageIndex = 1;
        }
        if (pageSize == null) {
            pageSize = 6;
        }
        if (pageSize >= 20) {
            pageSize = 20;
        }
        if (desc == null) {
            desc = true;
        }
        if (endTime == null) {
            endTime = System.currentTimeMillis();
        }
        if (startTime == null) {
            startTime = endTime - 1000 * 60 * 5;
        }
        if (endTime - startTime > maxQueryIntervalMs) {
            return Result.ofFail(-1, "time intervalMs is too big, must <= 1h");
        }
        List<String> resources = metricStore.listResourcesOfApp(app);
        logger.debug("queryTopResourceMetric(), resources.size()={}", resources.size());

        if (resources == null || resources.isEmpty()) {
            return Result.ofSuccess(null);
        }
        if (!desc) {
            Collections.reverse(resources);
        }
        if (StringUtil.isNotEmpty(searchKey)) {
            List<String> searched = new ArrayList<>();
            for (String resource : resources) {
                if (resource.contains(searchKey)) {
                    searched.add(resource);
                }
            }
            resources = searched;
        }
        int totalPage = (resources.size() + pageSize - 1) / pageSize;
        List<String> topResource = new ArrayList<>();
        if (pageIndex <= totalPage) {
            topResource = resources.subList((pageIndex - 1) * pageSize,
                    Math.min(pageIndex * pageSize, resources.size()));
        }
        final Map<String, Iterable<MetricVo>> map = new ConcurrentHashMap<>();
        logger.debug("topResource={}", topResource);
        long time = System.currentTimeMillis();
        for (final String resource : topResource) {
            List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(
                    app, resource, startTime, endTime);
            logger.debug("resource={}, entities.size()={}", resource, entities == null ? "null" : entities.size());
            List<MetricVo> vos = MetricVo.fromMetricEntities(entities, resource);
            Iterable<MetricVo> vosSorted = sortMetricVoAndDistinct(vos);
            map.put(resource, vosSorted);
        }
        logger.debug("queryTopResourceMetric() total query time={} ms", System.currentTimeMillis() - time);
        Map<String, Object> resultMap = new HashMap<>(16);
        resultMap.put("totalCount", resources.size());
        resultMap.put("totalPage", totalPage);
        resultMap.put("pageIndex", pageIndex);
        resultMap.put("pageSize", pageSize);



        Map<String, Iterable<MetricVo>> map2 = new LinkedHashMap<>();
        // order matters.
        for (String identity : topResource) {
            map2.put(identity, map.get(identity));
        }
        resultMap.put("metric", map2);
        return Result.ofSuccess(resultMap);
    }




/**
 *{"success":true,"code":0,"msg":"success","data":{"pageIndex":1,"metric":{"/testB":[{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314274000,"gmtCreate":1649314278520,"resource":"/testB","passQps":1,"blockQps":0,"successQps":1,"exceptionQps":0,"rt":6.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314276000,"gmtCreate":1649314278520,"resource":"/testB","passQps":2,"blockQps":0,"successQps":2,"exceptionQps":0,"rt":2.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314277000,"gmtCreate":1649314285533,"resource":"/testB","passQps":8,"blockQps":0,"successQps":8,"exceptionQps":0,"rt":2.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314278000,"gmtCreate":1649314285533,"resource":"/testB","passQps":8,"blockQps":0,"successQps":8,"exceptionQps":0,"rt":1.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314279000,"gmtCreate":1649314285533,"resource":"/testB","passQps":6,"blockQps":0,"successQps":6,"exceptionQps":0,"rt":1.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314280000,"gmtCreate":1649314285533,"resource":"/testB","passQps":4,"blockQps":0,"successQps":4,"exceptionQps":0,"rt":2.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314281000,"gmtCreate":1649314285533,"resource":"/testB","passQps":2,"blockQps":0,"successQps":2,"exceptionQps":0,"rt":2.0,"count":1}],"/testA":[{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314266000,"gmtCreate":1649314272789,"resource":"/testA","passQps":1,"blockQps":0,"successQps":0,"exceptionQps":0,"rt":0.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314267000,"gmtCreate":1649314272789,"resource":"/testA","passQps":6,"blockQps":0,"successQps":7,"exceptionQps":0,"rt":2.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314268000,"gmtCreate":1649314272789,"resource":"/testA","passQps":7,"blockQps":0,"successQps":7,"exceptionQps":0,"rt":2.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314269000,"gmtCreate":1649314272789,"resource":"/testA","passQps":7,"blockQps":0,"successQps":7,"exceptionQps":0,"rt":2.0,"count":1},{"id":null,"app":"cloudalibaba-sentinel-service","timestamp":1649314270000,"gmtCreate":1649314278520,"resource":"/testA","passQps":3,"blockQps":0,"successQps":3,"exceptionQps":0,"rt":1.0,"count":1}]},"totalPage":1,"pageSize":6,"totalCount":2}}
 * {
 *     "success": true,
 *     "code": 0,
 *     "msg": "success",
 *     "data": {
 *         "pageIndex": 1,
 *         "metric": {
 *             "/TransFormers": [
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641630000,
 *                     "gmtCreate": 1648641634048,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 85.0,
 *                     "count": 1
 *                 },
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641632000,
 *                     "gmtCreate": 1648641634048,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 7.0,
 *                     "count": 1
 *                 },
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641634000,
 *                     "gmtCreate": 1648641641050,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 7.0,
 *                     "count": 1
 *                 },
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641635000,
 *                     "gmtCreate": 1648641641050,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 8.0,
 *                     "count": 1
 *                 },
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641636000,
 *                     "gmtCreate": 1648641641050,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 9.0,
 *                     "count": 1
 *                 },
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641637000,
 *                     "gmtCreate": 1648641641050,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 9.0,
 *                     "count": 1
 *                 },
 *                 {
 *                     "id": null,
 *                     "app": "DATA-TRANSFORM-SERVICE",
 *                     "timestamp": 1648641638000,
 *                     "gmtCreate": 1648641641050,
 *                     "resource": "/TransFormers",
 *                     "passQps": 1,
 *                     "blockQps": 0,
 *                     "successQps": 1,
 *                     "exceptionQps": 0,
 *                     "rt": 9.0,
 *                     "count": 1
 *                 }
 *             ]
 *         },
 *         "totalPage": 1,
 *         "pageSize": 6,
 *         "totalCount": 1
 *     }
 * }
 *
 *
 *
 * */


    /**
     *
     * {
     *   "success": true,
     *   "code": 0,
     *   "msg": "success",
     *   "data": {
     *     "pageIndex": 1,
     *     "metric": {
     *       "/sys/menu/nav": [
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575171000,
     *           "gmtCreate": 1649575177604,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575172000,
     *           "gmtCreate": 1649575177604,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 24,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575174000,
     *           "gmtCreate": 1649575177604,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 12,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575175000,
     *           "gmtCreate": 1649575183608,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 10,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575176000,
     *           "gmtCreate": 1649575183608,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 9,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575216000,
     *           "gmtCreate": 1649575218613,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 9,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575219000,
     *           "gmtCreate": 1649575225608,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 9,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575277000,
     *           "gmtCreate": 1649575281607,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 9,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575288000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 7,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575291000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 9,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575292000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 7,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575293000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 8,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575294000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 7,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575295000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 7,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575296000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 6,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575297000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/menu/nav",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 7,
     *           "count": 1
     *         }
     *       ],
     *       "/sys/user/info": [
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575172000,
     *           "gmtCreate": 1649575177604,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 12,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575174000,
     *           "gmtCreate": 1649575177604,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575175000,
     *           "gmtCreate": 1649575183608,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 5,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575176000,
     *           "gmtCreate": 1649575183608,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 6,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575217000,
     *           "gmtCreate": 1649575225608,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 5,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575219000,
     *           "gmtCreate": 1649575225608,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575277000,
     *           "gmtCreate": 1649575281607,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 6,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575288000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 5,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575291000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 5,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575292000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575293000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575294000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575295000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 5,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575296000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 4,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575297000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/sys/user/info",
     *           "passQps": 2,
     *           "blockQps": 0,
     *           "successQps": 2,
     *           "exceptionQps": 0,
     *           "rt": 5,
     *           "count": 1
     *         }
     *       ],
     *       "/generator/datadevice/sys/listt": [
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575214000,
     *           "gmtCreate": 1649575218613,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 20,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575218000,
     *           "gmtCreate": 1649575225608,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 17,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575219000,
     *           "gmtCreate": 1649575225608,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 22,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575277000,
     *           "gmtCreate": 1649575281607,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 0,
     *           "exceptionQps": 0,
     *           "rt": 0,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575278000,
     *           "gmtCreate": 1649575281607,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 0,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 791,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575288000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 21,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575291000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 20,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575292000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 18,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575293000,
     *           "gmtCreate": 1649575295606,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 20,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575294000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 16,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575295000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 16,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575296000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 15,
     *           "count": 1
     *         },
     *         {
     *           "id": null,
     *           "app": "demo-main-service",
     *           "timestamp": 1649575297000,
     *           "gmtCreate": 1649575302598,
     *           "resource": "/generator/datadevice/sys/listt",
     *           "passQps": 1,
     *           "blockQps": 0,
     *           "successQps": 1,
     *           "exceptionQps": 0,
     *           "rt": 17,
     *           "count": 1
     *         }
     *       ]
     *     },
     *     "totalPage": 1,
     *     "pageSize": 6,
     *     "totalCount": 3
     *   }
     * }
     */









    @ResponseBody
    @RequestMapping("/queryByAppAndResource.json")
    public Result<?> queryByAppAndResource(String app, String identity, Long startTime, Long endTime) {
        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isEmpty(identity)) {
            return Result.ofFail(-1, "identity can't be null or empty");
        }
        if (endTime == null) {
            endTime = System.currentTimeMillis();
        }
        if (startTime == null) {
            startTime = endTime - 1000 * 60;
        }
        if (endTime - startTime > maxQueryIntervalMs) {
            return Result.ofFail(-1, "time intervalMs is too big, must <= 1h");
        }
        List<MetricEntity> entities = metricStore.queryByAppAndResourceBetween(
            app, identity, startTime, endTime);
        //@param identity 真正需要查找的资源名
        List<MetricVo> vos = MetricVo.fromMetricEntities(entities, identity);
        return Result.ofSuccess(sortMetricVoAndDistinct(vos));
    }

    private Iterable<MetricVo> sortMetricVoAndDistinct(List<MetricVo> vos) {
        if (vos == null) {
            return null;
        }
        Map<Long, MetricVo> map = new TreeMap<>();
        for (MetricVo vo : vos) {
            MetricVo oldVo = map.get(vo.getTimestamp());
            if (oldVo == null || vo.getGmtCreate() > oldVo.getGmtCreate()) {
                map.put(vo.getTimestamp(), vo);
            }
        }
        return map.values();
    }
}

/**
 * cloudalibaba-sentinel-service
 *{
 *     "success": true,
 *     "code": 0,
 *     "msg": "success",
 *     "data": []
 * }
 * */